Archive for tag: MVC 4

Datastyrede routes i ASP.NET MVC

I disse tider med større og større fokus på SEO-venlighed, kan det være rart, hvis man kan basere sine url'er på data fra eksempelvis sin database.

Man kunne have en produktdatabase, hvor produktet har en unik (url-venlig) streng, som kan anvendes til at finde produktet. I et setup uden nogen specielle regler, kan dette være en udfordring at få gjort stømlinet. Med en routedefinition som denne:

routes.MapRoute(
    name: "Default",
    url: "{controller}/{action}/{id}",
    defaults: new { 
        controller = "Home", 
        action = "Index", 
        id = UrlParameter.Optional 
    }
);

hvor produkt-controlleren hedder ProduktController, action-metoden til at vise et produkt hedder Vis og produktets urlnavn ligger i id-parameteren, kunne url'en til et produkt se således ud:

/produkt/vis/stor-tallerken-med-sort-kant

Det kan være fint nok ift. SEO, men det ville jo være rart, hvis man kunne slippe for de lidt overflødige controller- og action-tekster, så url'en kom til at se således ud i stedet:

/stor-tallerken-med-sort-kant

Dette kan opnåes med en RouteConstraint og en wildcard-urlparameter. Route-mapping kunne derfor se således ud:

routes.MapRoute(
    name: "Default",
    url: "{id}",
    defaults: new { controller = "Produkt", action = "Vis" },
    constraints: new { id = new ProduktRouteConstraint() }
);

Nu kaldes ProduktRouteConstraint's Match-metode hver gang routing kaldes. Denne Match-metode skal så håndtere et opslag i produktdatabasen for at se, om den angivne id refererer til et eksisterende produkt. Match-metoden returnerer en bool, der indikerer om den aktuelle route skal anvendes eller ej, dvs. om produktet findes i dette tilfælde. En implementering kunne se således ud:

public class ProduktRouteConstraint : IRouteConstraint
{
    public bool Match(
            HttpContextBase httpContext,
            Route route,
            string parameterName,
            RouteValueDictionary values,
            RouteDirection routeDirection
        )
    {
        var svc = new ProduktService();
        var id = values["id"].ToString();
        return svc.FindesProduktMedUrlnavn(id);
    }
}

Match-metoden fyrer op under en produktservice-klasse, der, via metoden FindesProduktMedUrlnavn, kan svare på, om produktet med det angivne navn (fra url'en) findes eller ej.

Hvis url'en ikke er et match, fortsætter routing-systemet videre til næste regel (som det altid har gjort). Derfor skal denne regel helst ikke være den sidste regel i rækken af routing-regler. 

En ting man skal være opmærksom på med denne metode er, at routing-systemet aktiveres hver gang der foretages et request til sitet. Derfor skal routingconstraints helst være så optimerede som muligt, da sitet ellers vil virke langsomt. Det vil med andre ord være en god idé, at overveje caching af grundlaget for disse specielle routingregler, i dette eksempel produktets urlnavne.

Dynamisk opdatering af resursefiler i ASP.NET

I forbindese med at jeg skal lave et site der understøtter flere sprog, har jeg kastet mig ud i et forsøg på at bruge resursefiler til at styre sprog/tekster.

Dette er jo oplagt, da resurserne er let tilgængelige i koden og i views. Desuden er der en indbygget integration mellem MVC-frameworkets DataAnnotations og resursefilerne, som ligeledes er let tilgængelig.

Udfordringer

Alt er, indtil videre, fint og sprogstyringen kører som sådan smertefrit. Jeg har dog behov for at kunne vedligeholde teksterne via mit administrationssystem og dermed mindst at kunne opdatere eksisterende tekster uden at skulle bygge min applikation og uploade igen.

Uden de store problemer formår jeg, dynamisk, at få indlæst de forskellige sprogversioner af resursefilerne og få dem vist i mit administrationssystem. Men så opstår udfordringerne, idet jeg jo gerne vil gemme mine ændringer i resursefilerne igen. Selvom det også går relativt smertefrit, at gemme mine ændringer i resx-filerne, opstår der i den forbindelse dog lige et lille problem: Mit site holder op med at svare. Frustrerende!

Det viser sig så, at når resx-filen opdateres, så bygger sitet automatisk systemet igen og dermed bliver sitet "lagt ned" i den periode dette sker. I mit tilfælde, hvor jeg kører en Azure Emulator, lader det ikke til at sitet kommer på benene igen overhovedet, men det er ikke noget jeg er helt sikker på (manglende tålmodighed til at vente længe nok).

Denne genstart medfører desuden, at alt hvad der hedder session-data slettes, fordi applikationen genstartes. Dette kan være ret uhensigtsmæssig - med mindre der er opsat distribueret caching af session, eller man slet ikke benytter session-state (som mig) ;-)

En delvis løsning

Det kan tilsyneladende ikke lade sig gøre at undgå denne bygning af koden, med mindre man går på kompromis med nogle af de integrationer til resursefiler, der er rundt omkring i frameworket. Desværre har jeg ikke fundet en løsning der er 100% optimal, men noget der måske kunne bruges er "Updatable Resources", som omgår de File Change Notifications, som medfører af applikationen bygges ved ændringer i f.eks. resx-filerne.

Jeg er klar over, at artiklen er noget gammel (2009), men jeg har ikke kunnet finde noget information, der var mere up to date, ej heller nogle der omhandlede MVC 4, som jeg pt. arbejder i.

Jeg har fået opfattelsen af, at det ikke er muligt at lave sin egen resursefabrik, således den smertefrit integrerer med MVC-frameworkets DataAnnotation, på samme måde, som den indbyggede. Det undrer mig bare, at dette ikke skulle være muligt. Det er muligt, at dette problem ikke eksisterer længere (der er trods alt løbet meget vand under broen siden 2009!). Umiddelbart ville jeg jo forvente, at resurser, ligesom så mange andre elementer i .NET, var pluggable og dermed kunne udskiftes efter behov. Spørgsmålet er bare hvor man skal proppe sine ændringer ind...(?)

Se også

Rick Strahl's gennemgang af en hjemmestrikket ResourceProvider der henter resurserne fra en database:  http://www.west-wind.com/presentations/wwdbResourceProvider/